#pragma once
#include <iostream>
#include <cstring>
#include "Log.hpp"
#include "ip_net_address.hpp"
// OS
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

using SockShPtr = std::shared_ptr<Socket>; //==typedef

static const int gsockfd_default = -1;
static const int gserve_ip = -1;
static const int gblocklog = 8;

enum // 匿名enum类型
{
    CreateSocket_ERROR
};

class Socket
{
public:
    virtual void CreateSocket() = 0;
    virtual void SockaddrBindSocket(const uint16_t port) = 0;
    virtual void ListenSocket() = 0;
    virtual SockShPtr AcceptSocket(InetAdress *inet_addr) = 0;
    virtual bool ConnectSocket(const std::string &ip, const uint16_t port) = 0;
    virtual ssize_t Recv(std::string *out) = 0;
    virtual ssize_t Send(const std::string &in) = 0;

public:
    void BuildListenSocket(const uint16_t port)
    {
        CreateSocket();
        SockaddrBindSocket(port);
        ListenSocket();
    }
    void BuildConnectSocket(const std::string &ip, const uint16_t port)
    {
        CreateSocket();
        ConnectSocket(ip, port);
    }
};

class TcpSocket : public Socket
{
public:
    TcpSocket()
        : _sockfd(gsockfd_default)
    {
    }

    void CreateSocket() override
    {
        _sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_sockfd >= 0)
        {
            LOG(INFO, "socket success");
        }
        else
        {
            LOG(FATAL, "socket fail");
            exit(CreateSocket_ERROR);
        }
    }

    void SockaddrBindSocket(const uint16_t port) override
    {
        struct sockaddr_in local_sockaddr;
        int len = sizeof(sockaddr);
        local_sockaddr.sin_family = AF_INET;
        local_sockaddr.sin_addr.s_addr = INADDR_ANY;
        local_sockaddr.sin_port = htons(port);
        int re = ::bind(_sockfd, (struct sockaddr *)&local_sockaddr, len);
        if (re >= 0)
        {
            LOG(INFO, "bind success");
        }
        else
        {
            LOG(FATAL, "bind fail");
            exit(-1);
        }
    }

    void ListenSocket() override
    {
        if (::listen(_sockfd, gblocklog) == 0)
        {
            LOG(INFO, "listen success");
        }
        else
        {
            LOG(FATAL, "listen fail");
            exit(-1);
        }
    }

    SockShPtr AcceptSocket(InetAdress *inet_addr) override
    {
        // 接收client的链接
        struct sockaddr peer;
        uint len = sizeof(peer);
        bzero(&peer, len); // memset(&peer, 0, len);
        int re = ::accept(_sockfd, (struct sockaddr *)&peer, &len);
        if (re >= 0)
        {
            LOG(INFO, "accept success");
        }
        else
        {
            LOG(FATAL, "accept fail");
            exit(-1);
        }
    }
    bool ConnectSocket(const std::string &ip, const uint16_t port) override
    {
        struct sockaddr_in server_sockaddr;
        int len = sizeof(server_sockaddr);
        server_sockaddr.sin_family = AF_INET;
        ::inet_pton(AF_INET, ip.c_str(), &server_sockaddr.sin_addr.s_addr);
        server_sockaddr.sin_port = htons(port);
        int re = ::connect(_sockfd, (struct sockaddr *)&server_sockaddr, len);
        if (re >= 0)
        {
            LOG(INFO, "connect successs");
            return true;
        }
        else
        {
            LOG(FATAL, "connect fail");
            return false;
        }
    }

    ssize_t Recv(std::string *out) override
    {
        char buffer[1024] = {0};
        int n = recv(_sockfd, buffer, sizeof(buffer), 0);
        if (n > 0)
        {
            buffer[n] = 0;
            *out += buffer;
        }
        else if (n == 0) // 对方关闭
        {
            LOG(INFO, "Recv success");
        }
        else
        {
            LOG(FATAL, "Recv fail");
        }
        return n;
    }
    ssize_t Send(const std::string &in) override
    {
        int n = send(_sockfd, in.c_str(), sizeof(in), 0);
        if (n == 0) // 对方关闭
        {
            LOG(INFO, "Recv success");
        }
        else
        {
            LOG(FATAL, "Recv fail");
        }
        return n;
    }

    int GetSockfd()
    {
        return _sockfd;
    }

    void Close()
    {
        close(_sockfd);
    }

private:
    int _sockfd;
};